use mio::net::TcpListener;
use mio::{Events, Interest, Poll, Token};
use std::collections::HashMap;
use std::io::Read;
use std::{io, thread};

#[test]
fn test_example() {
    const MAX_SOCKETS: usize = 32;

    const LISTENER: Token = Token(1024);

    let mut sockets = HashMap::new();

    let mut nex_socket_index = 0;

    let mut poll = Poll::new();

    let mut listener = TcpListener::bind("127.0.0.1:0".parse()?)?;

    poll.registry()
        .register(&mut listener, LISTENER, Interest::READABLE)?;

    let addr = listener.local_addr()?;

    thread::spawn(move || {
        use std::net::TcpStream;
        for _ in 0..(MAX_SOCKETS + 1) {
            let _ = TcpStream::connect(addr).unwrap();
        }
    });

    let mut events = Events::with_capacity(1024);

    let mut buf = [0; 256];

    loop {
        poll.poll(&mut events, None)?;

        for event in &events {
            match event.token() {
                LISTENER => match listener.accept() {
                    Ok((mut socket, _)) => {
                        if nex_socket_index == MAX_SOCKETS {
                            return Ok(());
                        }

                        let token = Token(nex_socket_index);
                        nex_socket_index += 1;

                        poll.registry()
                            .register(&mut socket, token, Interest::READABLE)?;

                        sockets.insert(token, socket);
                    }
                    Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                        break;
                    }
                    e => panic!("err"),
                },
                token => {
                    loop {
                        match sockets.get_mut(&token).unwrap().read(&mut buf) {
                            Ok(0) => {
                                sockets.remove(&token);
                                break;
                            }
                            // Data is not actually sent in this example
                            Ok(_) => unreachable!(),
                            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                                break;
                            }
                            e => panic!("err={:?}", e),
                        }
                    }
                }
            }
        }
    }
}

#[test]
fn test_token() {
    use std::collections::HashMap;

    let mut map = HashMap::<Token, usize>::new();
    map.insert(Token(1), 1);
    map.insert(Token(2), 2);
    map.insert(Token(3), 3);

    assert_eq!(map.get(&Token(1)), Some(&1));
    assert_eq!(map.get(&Token(2)), Some(&2));
}
